<?php
/**
 * ModelFactory(モジュール名はModelなので注意)
 *
 * 標準モデルモジュール
 * 厳密に言えばこれ自体はモデルデリゲータもしくはファクトリで、
 * これによって生成されたオブジェクトがモデル実態となる。
 * 
 * @since 2012/02/13
 * 
 */
class ModelFactory {
	private $_modelDir = '';
	
	public function __construct () {
		$module = Y('Module')->add($this, 'Model');
		$module->add (&$this->_modelDir, 'ModelDir');
		$module->add (Y::func($this, 'loadModel'), 'Load');
		$module->add (Y::func($this, 'newModel'), 'New');
		$module->add (Y::func($this, 'connectModel'), 'Connector');
	}

	/**
	 * モデル定義ファイルを読み込む
	 * モデル自体は定義ファイルで作成するので、これの実態はただのinclude_once。
	 * 
	 * @since 2012/02/13
	 */
	public function loadModel ($file) {
		Y('Log.d')->call('モデルファイルの読み込み:'.$file);
		$fileName = $this->_modelDir.'/'.$file.'.php';
		if (is_file ($fileName)) {
			include_once $fileName;
			return true;
		}
		return false;
	}

	/**
	 * モデルファクトリメソッド
	 * 新しいモデルを作成する
	 * 実際にはModelクラス側のfactoryメソッドを呼び出すだけ
	 * 
	 * @since 2012/02/13
	 */
	public function newModel ($name, $type) {
		Y('Log.d')->call('新規モデル生成開始:'.$name);
		return Y('Model')->add(Model::factory ($name, $type), $name);
	}
	
	/**
	 * コネクタを生成して呼び出す。
	 * 生成済みのコネクタは同一のものが使われるので改めて生成はされない。
	 * 
	 * @since 2012/02/13
	 */
	public function connectModel ($type) {
		
	}
}

/**
 * Model実態クラス
 * これはモデル毎に作成される
 * コネクタの型ごとに動作が違うが、ぶっちゃけこれ自体はコネクタに処理を渡すだけのものなので 
 * 実際には生成される子オブジェクトが違うだけ。
 * 
 * @since 2012/02/13
 */
class Model {
	/**
	 * コンストラクタ
	 * とりあえず作っただけ
	 * 
	 * @since 2012/02/14
	 */
	public function __construct () {
		
	}
	
	/**
	 * factoryメソッド
	 * 基本的にこれを使用してモデルを生成する。
	 * 別にModule.Model.newを使わなくてもこれを直接呼び出して問題ない。
	 * 
	 * @since 2012/02/14
	 * @return YuTori 生成されたモデルのYuToriオブジェクト
	 */
	static function factory ($name, $type) {
		$instance = new Model ();
		$model = YuTori::factory ($name, $instance);
		// typeはコネクタオブジェクト（メソッド？）なので、クラスメンバには入れない
		$model->add ($type, 'Type');	// 暫定
		$model->add (array(), 'Col');
		$model->add (array(), 'Row');
		$model->add (array(), 'Input');		// バリデート済の値群
		$model->add (Y::func($instance, 'validate'), 'Validate');
		$model->add (Y::func($instance, 'addRule'), 'addRule');
		switch (strtolower ($type)) {
			case ('single'):
			default:
				break;
		}
		return $model;
	}
	
	/**
	 * バリデータにルールを追加する
	 * 実際にはCallable型としてValidate配下にaddしているだけなので、
	 * 直接addした方が素直なコードになる場合もある。
	 * 
	 * ちなみに標準ルールにしか使用できないので注意。
	 * 
	 * @since 2012/02/16
	 */
	public function addRule ($col, $type, $args = array()) {
		$validate = Y('this')->neighbor('Validate');
		($input = $validate->child($col, false)) || ($input = $validate->add($col, array()));
		if (!$validator->isEmpty()) $input->add (Y::func($this, 'validateRules_'.$type), null, $args);
	}

	/**
	 * バリデータ
	 * 引数に入れられた配列からcolsと同じキーを取得して、
	 * 子オブジェクトのcall引数として渡す。
	 * callがfalseを返した場合、そこでエラーとしてストップする。
	 * 参照渡しするので、値の変換も可能。
	 * 
	 * @since 2012/02/14
	 */
	public function validate (YuTori $values) {
		if ($log = Y('Log.d', false)) Y('Log.d')->call('バリデート開始');
		$now = Y('this');
		$model = $now->parent(0);
		$inputs = $model->add($values->call(), 'InputNow');	// 値を移し替える
		$error = $model->add (false, 'Error');
		$i = 0;
		while($validate = $now->child($i++, false)) {
			$name = $validate->getName();
			$input = $inputs->child($name);
			$error->add ($name, null);
			$log && $log->call('チェック開始 : '.$input->getName());
			$log && $log->call('値 : '.$input->call());
			$n = 0;
			while ($validator = $validate->child($n++, false)) {
				// バリデータのcallは入力値・モデル実態の順
				if (!$validator->call($input, $model)) {
					$error->set (true);
					break;
				}
			}
		}
		if ($error->call()) {
			$log && $log->call('チェック結果 : エラー');
			return false;
		} else {
			$log && $log->call('チェック結果 : 通過');
			$model->child('Input')->add($input->call(), null);
			return true;
		}
	}
	
	/*
	 * バリデート処理メソッド群
	 */
	/**
	 * 必須項目
	 * 内容が空の時falseを返す。
	 * 
	 * @since 2012/02/17
	 */
	public function validateRules_NotNull (YuTori $input, YuTori $model) {
		if ($log = Y('Log.d', false)) $log->call('必須項目チェック');
		$validate = Y('this');
		if ($input->isNull()) {
			$model->child('Error.'.$input->getName())->set($validate->child('Error')->sput($input->getName().' is required.'));
			$log && $log->call('エラー:必須項目未入力');
			return false;
		} else {
			return true;
		}
	}
}

new ModelFactory ();
